UNIT — FILE & EXCEPTION HANDLING

Python File &
Exception Handling

A comprehensive lecture for BTech Computer Science students

Course: Python Programming Level: BTech II Year Topic: I/O & Error Handling
01

Introduction to File Handling

💾 What is a File?

A file is a named location on disk that stores related data permanently. Unlike variables, file data persists even after the program terminates.

🔑 Why File Handling?

  • Persist data beyond program lifetime
  • Share data between programs
  • Process large datasets
  • Log events and errors

📂 Types of Files

  • Text files — .txt, .csv, .py
  • Binary files — .jpg, .mp3, .exe

Python handles both; today we focus on text files.

âš™ī¸ File Operations — The Three-Step Rule

1. Open the file
──â–ļ
2. Read / Write
──â–ļ
3. Close the file

Forgetting to close a file can cause data corruption and resource leaks. Always close files — or better, use the with statement which closes automatically.

02

File Modes

ModeSymbolDescriptionFile Exists?File Missing?
ReadrRead-only; file pointer at beginning✅ Open❌ Error
WritewWrite-only; truncates existing contentâš ī¸ Overwrite✅ Create
AppendaWrite; appends to end of file✅ Append✅ Create
Read+Writer+Read and write; no truncation✅ Open❌ Error
Write+Readw+Read and write; truncates fileâš ī¸ Overwrite✅ Create
Binary ReadrbRead binary file✅ Open❌ Error
Binary WritewbWrite binary fileâš ī¸ Overwrite✅ Create
PYTHON — OPENING FILES
# Method 1: Traditional — must manually close f = open("name.txt", "r") data = f.read() f.close() # âš ī¸ Don't forget this! # Method 2: Context Manager (with) — RECOMMENDED ✅ with open("name.txt", "r") as f: data = f.read() # File is automatically closed here, even if exception occurs # Opening with encoding (always specify for text files) with open("name.txt", "r", encoding="utf-8") as f: data = f.read()
03

File Operations

📖 Reading from Files

PYTHON — READING METHODS
with open("name.txt", "r") as f: # read() — reads entire file as one string content = f.read() print(content) # readline() — reads one line at a time f.seek(0) # go back to beginning line = f.readline() print(line) # readlines() — reads all lines, returns list f.seek(0) lines = f.readlines() print(lines) # ['Alice\n', 'Bob\n', 'Charlie\n'] # Iterating line by line (most memory-efficient) f.seek(0) for line in f: print(line.strip()) # strip() removes \n

âœī¸ Writing to Files

PYTHON — WRITING METHODS
# write() — writes a string (does NOT add newline automatically) with open("name.txt", "w") as f: f.write("Alice\n") f.write("Bob\n") f.write("Charlie\n") # writelines() — writes a list of strings names = ["Alice\n", "Bob\n", "Charlie\n"] with open("name.txt", "w") as f: f.writelines(names) # Append mode — does not erase existing content with open("name.txt", "a") as f: f.write("David\n") # adds to end
MethodDescriptionReturns
read(n)Read n characters (or all if n omitted)str
readline()Read one line including \nstr
readlines()Read all lines into a listlist
write(s)Write string s to fileint (chars written)
writelines(lst)Write list of stringsNone
seek(pos)Move file pointer to positionint
tell()Return current file pointer positionint
flush()Flush the write bufferNone
close()Close the fileNone
04

Python Exceptions

⚡ What is an Exception?

An exception is an event that disrupts normal program flow during runtime. Unlike syntax errors (caught at compile time), exceptions occur during execution.

🧱 Exception Hierarchy

  • BaseException → root of all
  • Exception → most user exceptions
  • ArithmeticError → ZeroDivisionError etc.
  • LookupError → IndexError, KeyError
  • OSError → FileNotFoundError etc.
ExceptionCauseExample
ZeroDivisionErrorDivision by zero10 / 0
ValueErrorInvalid value for operationint("abc")
TypeErrorWrong data type used"a" + 1
FileNotFoundErrorFile does not existopen("x.txt")
IndexErrorList index out of rangelst[100]
KeyErrorDict key not foundd["x"]
NameErrorVariable not definedprint(x)
AttributeErrorAttribute not found"hi".push()
ImportErrorModule not foundimport xyz
PermissionErrorFile access deniedopen("/root/x")
MemoryErrorOut of memory[1]*10**10
RecursionErrorMax recursion depth exceededInfinite recursion
05

Try-Except Block Structure

PYTHON — FULL TRY-EXCEPT-ELSE-FINALLY SYNTAX
try: # Code that might raise an exception result = 10 / 0 except ZeroDivisionError as e: # Handle a specific exception print(f"Error: {e}") except (TypeError, ValueError) as e: # Handle multiple exceptions in one block print(f"Type or Value Error: {e}") except Exception as e: # Catch-all for any other exception print(f"Unexpected error: {e}") else: # Runs ONLY if no exception occurred in try block print("Success! Result =", result) finally: # ALWAYS runs — use for cleanup (closing files, DB, etc.) print("This always executes.")

đŸŸĸ try

Contains the code that might raise an exception. Python monitors this block. If an exception occurs, execution jumps to the matching except.

🔴 except

Handles the exception. You can have multiple except blocks for different exception types. Executed only when an exception matches.

✅ else

Runs only when the try block completes successfully — no exception. Useful for code that should run only on success.

🔒 finally

Always executes — whether an exception occurred or not. Use for cleanup tasks like closing files, releasing locks.

🚀 The raise Statement

PYTHON — RAISE EXCEPTIONS
# Raise a built-in exception manually def divide(a, b): if b == 0: raise ZeroDivisionError("Denominator cannot be zero!") return a / b # Re-raise an exception after partial handling try: divide(5, 0) except ZeroDivisionError: print("Caught it — but re-raising...") raise # propagates the exception upward
06

Custom Exceptions

User-defined exceptions let you create meaningful, domain-specific errors. Always inherit from Exception (or its subclasses). This allows your exceptions to be caught by generic except Exception blocks.

PYTHON — CUSTOM EXCEPTION CLASSES
# Basic custom exception class NegativeNumberError(Exception): pass # Custom exception with message and attributes class FileSizeError(Exception): def __init__(self, size, limit): self.size = size self.limit = limit super().__init__(f"File size {size}MB exceeds limit of {limit}MB") # Exception hierarchy for a file application class AppError(Exception): # Base for all app errors pass class InvalidDataError(AppError): # Bad data format pass class FileAccessError(AppError): # File access problem pass # Using custom exceptions def write_data(filename, data): if not isinstance(data, str): raise InvalidDataError("Data must be a string") try: with open(filename, "w") as f: f.write(data) except PermissionError: raise FileAccessError(f"Cannot write to {filename}") try: write_data("output.txt", 12345) # integer, not string! except InvalidDataError as e: print(f"Data Error: {e}") except FileAccessError as e: print(f"Access Error: {e}")

🔗 File Handling + Exception Handling Together

PYTHON — ROBUST FILE READING
def safe_read_file(filename): try: with open(filename, "r", encoding="utf-8") as f: return f.readlines() except FileNotFoundError: print(f"❌ '{filename}' not found.") except PermissionError: print(f"🔒 No permission to read '{filename}'.") except UnicodeDecodeError: print("âš ī¸ Cannot decode file — is it binary?") except OSError as e: print(f"OS Error: {e}") finally: print("Read attempt complete.") return [] # return empty list on failure lines = safe_read_file("name.txt")
07

Practice Exercises — Hints

â„šī¸ These are HINTS — not full solutions. Think through the logic, look up the methods used, and write the code yourself. The best learning is through struggle!
Q1 names.txt — Count, Vowel Names, Longest Name
âš™ī¸ Setup: Create "name.txt" with one name per line — e.g., Alice, Bob, Uma, Irfan...

a. Count total names

with open("name.txt", "r") as f: names = [line.strip() for line in f if line.strip()] # count = len(names) → Use len() on the list

b. Count names starting with a vowel

# After reading names list: vowels = 'AEIOUaeiou' # Use a generator/list comprehension: count = sum(1 for n in names if n[0] in vowels) span class="hl-cm"># Or: filter + len

c. Find the longest name

# Use the built-in max() with key parameter: longest = max(names, key=len) # Or manually: iterate and track max length + name
Q2 Integer File — Max, Average, Count > 100
âš™ī¸ Setup: Write integers (one per line) to "numbers.txt"

Key insight: numbers read from file are strings — convert!

with open("numbers.txt", "r") as f: nums = [int(line.strip()) for line in f if line.strip()] # a. Max number max_num = max(nums) # b. Average avg = sum(nums) / len(nums) # c. Count > 100 count = sum(1 for n in nums if n > 100)
Q3 city.txt — Display, Population Filter, Sum of Areas
âš™ī¸ Format each line: CityName Population Area (space-separated)

a. Display all cities

with open("city.txt", "r") as f: for line in f: parts = line.split() # ['Dehradun', '5.78', '308.20'] city, pop, area = parts[0], float(parts[1]), float(parts[2]) print(city, pop, area)

b. Cities with population > 10 lakhs

# After parsing each line: if pop > 10: print(city)

c. Sum of all areas

total_area = 0 # Inside the loop: total_area += area print("Total Area:", total_area)
Q4 Integer Division with ZeroDivisionError and ValueError Handling
âš™ī¸ Read N pairs of values; perform a//b; handle exceptions gracefully

Structure Hint

n = int(input()) for _ in range(n): try: a_str, b_str = input().split() a, b = int(a_str), int(b_str) # ValueError if non-integer result = a // b # ZeroDivisionError if b==0 print(result) except ZeroDivisionError as e: print("Error Code:", e) except ValueError as e: print("Error Code:", e)
💡 Key: int("$") raises ValueError. 5//0 raises ZeroDivisionError. Handle both separately!
Q5 Multiple Custom Exceptions for File Handling

Design multiple custom exceptions for a file program

# Define custom exception hierarchy class FileAppError(Exception): pass class EmptyFileError(FileAppError): pass class InvalidFormatError(FileAppError): pass class FileTooLargeError(FileAppError): pass def process_file(filename): import os if os.path.getsize(filename) == 0: raise EmptyFileError("File is empty!") if os.path.getsize(filename) > 1048576: # > 1MB raise FileTooLargeError("File exceeds 1MB!") with open(filename) as f: for line in f: if "|" not in line: # expect pipe-separated raise InvalidFormatError(f"Bad format: {line.strip()}") try: process_file("data.txt") except EmptyFileError as e: print("Empty:", e) except FileTooLargeError as e: print("Too large:", e) except InvalidFormatError as e: print("Format error:", e) except FileNotFoundError: print("File not found!")
Q6 Execution Counter — How Many Times Has the Program Run?
âš™ī¸ Store the counter persistently in a file. Each run reads, increments, writes back.

Logic Hint

# Step 1: Try to read existing count from file try: with open("counter.txt", "r") as f: count = int(f.read().strip()) except FileNotFoundError: count = 0 # first run — start from 0 except ValueError: count = 0 # file corrupted — reset # Step 2: Increment and write back count += 1 with open("counter.txt", "w") as f: f.write(str(count)) print(f"This program has been run {count} time(s).")
💡 Key concepts used: FileNotFoundError to handle first run, file read/write, int/str conversion.

📌 Quick Reference — Key Takeaways

CHEAT SHEET
# ALWAYS use with statement for files with open("file", "mode") as f: ... # Modes: r(read) w(write-overwrite) a(append) r+(read+write) # ALWAYS strip() when reading lines names = [l.strip() for l in f if l.strip()] # ALWAYS convert strings from file to numbers nums = [int(l.strip()) for l in f] # ALWAYS handle FileNotFoundError when reading try: with open("f") as f: ... except FileNotFoundError: ... # Custom exceptions → inherit from Exception class MyError(Exception): pass